第8章 実行時の構造:オブジェクト
1. オブジェクト
1. オブジェクトとは何か?
オブジェクトは何らかのクラスの実行時のインスタンスである。
上の定義以外の見方
外界のシステムに見られる「オブジェクト」
例:グラフィックシステム
点、線、面、立体、etc
例:給与計算システム
従業員、給与小切手、給与体系
オブジェクト指向によって外界のシステムを自然にモデル化できる
あるクラスは問題領域における外界のオブジェクトの型のモデルを意味する。
ただし、すべてのクラスが問題領域のオブジェクト型に対応するとは限らない。
2. 基本形式
Oをオブジェクトとする。オブジェクトは何らかのクラスCの直接インスタンスである。
CはOの生成クラス、あるいは、単にジェネレータと呼ばれる。
Cはソフトウェアテキスト、Oは実行時のデータ構造である。
オブジェクトの形式はクラスが持つ属性によって決まる。
オブジェクトはフィールド(個々の属性について一つずつ用意されている要素)の集合に過ぎない。
code:POINT
class POINT feature
x, y: REAL
-- ルーチンの宣言 --
end
3. 単純なフィールド
基本型
BOOLEAN
それぞれの論理値の真と偽を表すインスタンスを二つだけ持つ
TrueおよびFalse
CHARACTER
インスタンスは文字を表す
'A'のように一重引用符でその文字を囲む
STRING
インスタンスは有限の長さの文字列を表す。
"A STRING"のように文字の並びを二重引用符で囲む
INTEGER
インスタンスは整数を表す
34, -675, +4など
REALおよびDOUBLE
インスタンスは単制度および倍精度の浮動小数を表す
3.5,-0.05などDOUBLEの場合は-5.e-2のような表記も可能
基本型のインスタンスはコンピュータ上で効率的に実装される定義済み集合から値を取り出す
4. 本の単純な概念
例:クラス BOOK1
code:book
class BOOK1 feature
title: STRING
date, page_count: INTEGER
end
フィールドや構造型と違い、以下のようにフィールドに値を割り当てることはできない
5. 著者
例:クラス WRITER
code:writer
class WRITER feature
name, real_name: STRING
birth_year, death_year: INTEGER
end
6. 参照
サブオブジェクト
本には著者があるという特徴を表現したい
ほんのクラスBOOK2の中にそれ自体がオブジェクトであるフィールドauthorを設ける
例:サブオブジェクト「著者」を持つ二つの「本」のオブジェクト
code:BOOK
class BOOK2 feature
title: STRING
date, page_count: INTEGER
author: Writer
サブオブジェクトの問題点
メモリ領域の無駄
例えば、人を表すオブジェクトを考えたとき、それぞれは市民権を持っている国を表すサブオブジェクトを持っているとする。表現される人の数は多いが国の数は少ない。
共有(sharing)を表現できない
二つのオブジェクトのauthorフィールドはWRITERオブジェクトが更新されたら、その変更がその著者の関係するすべてのほんに反映されるようにしたい。
参照
参照とは実行時にvoid(無効)かアタッチ状態(attached)かのいずれかの値をとる、アタッチ状態の場合、参照は一つの次オブジェクトを示す。(その場合、その参照はその特定のオブジェクトにアタッチされているという)
7. オブジェクトのアイデンティティ
オブジェクト指向システムの実行中に作成されるオブジェクトには、そのフィールドによって定義されるオブジェクトの値とは無関係の、唯一のアイデンティティがある。
8. 参照を宣言する
code:BOOK3
class BOOK3 feature
title: STRING
date, page_count: INTEGER
author: WRITE -- これが新しい属性
end
クラスが次のような標準的な形式で宣言されているときには必ずclass C feature ..end
次の形式の宣言によってC型と宣言されたすべてのエンティティはx:CでC型のオブジェクトへの参照である値を表す。
9. 自己参照
対応するクラスの間の顧客関係にも循環がある
code:PERSON1
class PERSON1 feature
name: STRING
loved_one, landload: PERSON1
end
実行時のオブジェクト構造が複雑だからと言って、必ずしもソフトウェアそのもの、すなわち、クラス間の関係が複雑とは限らない。
クラス間の関係はむしろできる限り単純いするように心がけるべき
2. モデリングツールとしてのオブジェクト
1. ソフトウェア開発における四つの世界
モデル化される側のシステム
オブジェクトの型とその抽象的な関係によって記述される
外界システムの特定の具体例
関係する可能性のあるオブジェクトから構成される
ソフトウェアシステム
オブジェクト指向の関係によって結び付けられたクラスから構成される
オブジェクト構造
参照によって結び付けられたソフトウェアオブジェクトから構成される。
3. オブジェクトと参照を操作する
1. 動的な作成と再アタッチメント
柔軟なデータ構造を作成し、利用できるようにするために、オブジェクト指向によるソフトウェえあシステムがオブジェクトをどのように扱うか
動的な作成と再アタッチメント
実行時に必要に応じてオブジェクトをシステムに作らせる
最初の状態はオブジェクトが一つだけ(ルートオブジェクト)作られる
新しいオブジェクトを作成する
voidだった参照をオブジェクトにアタッチする
参照をvoidするすでにアタッチされていた参照を他のオブジェクトにアタッチする
2. 生成命令
単純な形式の生成命令: create x
xは生成命令のターゲットと呼ばれる。
BOOK3クラスの顧客クラス QUOATATIONの例
code:QUOTATION
class QUOTATION feature
source: BOOK3
page: INTEGER
make_book is
do create source end
end
すべてのフィールドはデフォルトで初期化される
3. 生成命令(2)
基本生成命令の効果
ターゲットxの型がクラスCに基づいた参照型であるとき、create xという形式の生成命令の効果は次の三つのステップを実行することである。
デフォルトの初期値
参照値のデフォルト値はvoidである。
BOOLEAN値のデフォルト値はFalseである。
CHARACTER値のデフォルトはヌルキャラクタである。
数値(INTEGER型、REAL型、DOUBLE型)のデフォルト値はゼロ(すなわり適切な型の値ゼロ)である。
4. なぜ明示的な生成が必要なのか?
生成と宣言は区別される
b: BOOK3 -- 宣言(オブジェクトは作られない)
create b -- 生成(オブジェクトが生成される)
宣言時にオブジェクトが生成されたとしたら
オブジェクトを一つ生成するためにたくさんのオブジェクトが生成されてしまう
特に自己参照クラスの場合、無限ループに陥る
void参照や一つのオブジェクトに複数の参照をアタッチする余地がなくなってしまう。
フィールドが作られるたびにオブジェクトを作成するのは意味がない
4. 生成プロシージャ
1. デフォルトの初期化に優先する初期化
デフォルトの初期値以外の初期化をするためには、そのクラスに一つ以上の生成プロシージャを記述する。
生成プロシージャとは、feature句の前のキーワードcerationで始まる句に列挙するプロシージャ
code:POINT1
class POINT1 creation
make_cartesioan, make_polor
feature
.. 前のバージョンに記述されている特性:
x, y, ro, theta, translate, scale, ...
feature {NONE}
make_cartesian(a, b:REAL) is
do x := a; y:= b end
make_polar(r, t: REAL) is
do x := r * cos(t); y := r * sin(t) end
end -- クラスPOINT1
顧客は次のような命令で点を生成する
create my_point.make_cartesian(0, 1)
create my_point.make_polar(1, Pi / 2)
生成呼び出しの効果
create x.p(...)という形式の生成呼び出しを実行すると、次の4つのステップが実行される。ただし、ターゲットxの型はクラスCに基づいた参照型であり、pはクラスCの生成プロシージャであり、(...)は必要ならば指定すべき、このプロシージャの実引数の有効な並びを表す。
C1: Cの新しいインスタンス(Cの個々の属性に対して一つずつ作られるフィールドの集合から構成される)を作成する。
C2: 標準のデフォルト値に従ってOCの各フィールドを初期化する。
C3: xの値(参照)をOCにアタッチする。
C4: 与えられた引数と共にOCに対してプロシージャpを呼び出す。
2. 生成プロシージャのエクスポートステータス
POINT1の二つの生成プロシージャはfeature{NONE}で始まる特性句で宣言されており、通常の呼び出しに対しては非公開である。
プロシージャの選択的な公開を設定することも可能
3. 生成プロシージャに関する規則
create xという基本形式とcreate x.p(...)という生成呼び出しの二つの形式の生成命令は互いに排他的である。
クラスの中にcraetion句が現れたら、許されるのは生成呼び出しだけで基本形式は無効
コンパイラによって拒絶されるため
オブジェクトの一貫性のため
生成命令は常に一貫したオブジェクトを生成しなければならない
呼び出しのないcreate xという基本形式の生成命令が許されるのは、すべてのフィールドにデフォルト値を設定することで一貫したオブジェクトが生成される場合だけ。
4. 複数の生成プロシージャと多重定義
C++やJavaでは、生成プロシージャはすべて同じ名前であり、クラス名そのものである。
二つのコンストラクタを区別するにはシグネチャで区別するしかない。
make_cartesianとmake_polarに相当するコンストラクタを同時に定義できない。
5. さらに参照について
1. 参照の状態
参照はvoidかアタッチされているかの二つの状態のいずれかである。
オブジェクト
実行時の概念。すべてのオブジェクトはある特定のクラスのインスタンスで、実行時作成され、いくつかのフィールドから構成される。
参照
実行時の概念。参照はvoidかオブジェクトにアタッチされているかのいずれかの値である。
エンティティ
静的な概念。クラステキストに現れる識別子である。
2. voidの参照と呼び出し
ターゲットがvoidの特性呼び出しを実行しようとすると例外が発生する
コンパイラでチェックすることは非現実的
簡単な方法
code:void
if x /= Void then
x.f(..)
else
...
end
ただし、すべての特性呼び出しに対して上記のように書くのは荷が重すぎる
システムが正確であることを証明するには以下を証明する必要がある。
voidの参照に適用される呼び出しが全くないこと
すべての表明が対応する実行時の瞬間に満たされていること
ソフトウェアを守る方法
ソフトウェアを書くときは、誤った状況が実行時に発生するのをあらゆる手段を使って防ぐ必要がある。
何らかの疑問が残っていて、同時に実行時の失敗が許されない場合、例外を処理するための用意をソフトウェアに備えておく。
6. 参照に対する操作
2. 参照の比較
等号演算子を使う
x=yが真になるのは、対応する参照が共にvoidであるか、同じオブジェクトにアタッチされている場合だけ
参照を比較するのであって、オブジェクトを比較するのではない
xとyが全く別のオブジェクトにアタッチされていれば、例えそれらのオブジェクトのフィールドが全く一致していたとしても、x=yの値は偽となる。
3. voidという値
定義済みの特性Void
特性の参照がvoidかどうかテストする
if x = Void then ...
voidの参照を作る
x := Void
4. オブジェクトのクローニングと一致
オブジェクトの新しいコピーを作る
x := clone(y)
オブジェクトの内容を比較する
equeal(x, y)
5. オブジェクトのコピー
すでにあるオブジェクトのフィールドに上書きしたい場合
x.copy(y)
yにアタッチされているオブジェクトのフィールドがxに対応するフィールドにコピーされる
xもyもvoidであってはならない
7. 深い記憶:永続性について
参照を含むオブジェクトに対するストレージと復元をどうすべきか?
オブジェクトにアタッチされている参照はそのオブジェクトなしでは価値がない
直接依存のオブジェクトと依存オブジェクト
オブジェクトの直接依存とは、そのオブジェクトの参照フィールドにアタッチされているオブジェクトがあれば、そのオブジェクトのことをいう。オブジェクトの依存オブジェクトは、そのオブジェクト自身と、そのオブジェクトの直接依存オブジェクトの依存オブジェクトである。
永続性の閉鎖の原則
ストレージメカニズムによってオブジェクトを保存するときには必ずそのオブジェクトの依存オブジェクトも一緒に保存しなければならない。保存されているオブジェクトを復元メカニズムによって取り出すとき、そのオブジェクトのまだ取り出されていない依存オブジェクトがある場合には、必ずそれらも取り出されなければならない。
STORABLEの基本的な特性の形式
store(f: IO_MEDIUM)
x.store(f)という形式の呼び出しを実行すると、xにアタッチされているオブジェクトがそのすべての依存オブジェクトにfに関連付けられているファイルに保存される
xの生成クラスはSTORABLEの子孫でなければならない
xにアタッチされているオブジェクトを保存される構造の銭湯オブジェクトという。
retrieved(f: IO_MEDIUM): STORABLE
retrived(f)の結果として、前のstore呼び出しによってfに保存された完全なオブジェクト構造と同一なオブジェクト構造が得られる。
7. 複合オブジェクトと拡張型
1. 参照は十分ではない
実行時の値がCのインスタンスそのものであるエンティティxが必要な場合、次のように宣言する
x: expanded C
あるオブジェクトOの一つまたは複数のフィールド自体がオブジェクトであるとき、Oは複合オブジェクトであるという
複合オブジェクトの書き方
code: COMPOSITE
class COMPOSITE feature
ref: C
sub: expanded C
end
2. 拡張型
拡張クラス
code:E
expanded class E feature
...
end
E型として宣言されるエンティティはすべて拡張型になる
定義:拡張型
次のような二つのい場合、その型は拡張型である
expanded Cという形式である
Eという形式である。ただし、Eは拡張クラスである。
3. 拡張型の役割
効率性を向上させる
より良いモデル化を可能にする
統一されたオブジェクト指向的体系の中での基本肩をサポートする
5. 拡張型の性質
E型の拡張エンティティxについて
xの値は常にオブジェクトなので、絶対にvoidにはならない
x = Voidの結果は常に偽になる
クラスDにexpanded C型の属性が含まれている場合、クラスCはexpanded D型の属性を持つことはできない
拡張顧客の規則
「拡張顧客」は次のように定義されたクラス間の関係であるとする。すなわち、Cのいずれかの属性がSに基づく拡張型であるならば、CはSの拡張顧客である。
従って、拡張顧客という関係に循環が含まれることがあってはならない。
6. サブオブジェクトへの参照はだめ
実装の側面
ガベージコレクションが効率よく実装できる
モデリングの視点
システム記述を単純化することになる
8. アタッチメント:参照の値と意味